NAVIGATION
  • 전체 글
CATEGORIES
  • Develop7
    • Backend3
    • Frontend2
    • Git1
    • Performance1
  • AI3
    • Claude Code3
  • Frontend1
    • Next.js1
POPULAR TAGS
  • API3
  • 클로드코드2
  • 성능최적화2
  • GraphQL2
  • REST2
  • React2
  • JavaScript2
  • 프론트엔드2
  • Claude Code1
  • AI1
···
>steady-one_

© 2025 steady-one Blog. All rights reserved.

Rest 비교용
Backend
2025년 10월 1일

Rest 비교용

GraphQL과 REST API의 차이점을 상세히 비교하고, 각 상황에 맞는 최적의 선택 방법을 알아봅니다.

.graphql.rest.api

GraphQL vs REST: 언제 무엇을 사용할까

API 설계는 현대 웹 애플리케이션 개발에서 가장 중요한 결정 중 하나입니다. REST API는 오랫동안 업계 표준으로 자리잡았지만, Facebook이 2015년 GraphQL을 오픈소스로 공개한 이후 많은 개발자들이 새로운 선택지를 고민하고 있습니다. 이 글에서는 두 접근 방식의 핵심 차이점을 깊이 있게 살펴보고, 프로젝트에 맞는 최적의 선택을 할 수 있도록 의사결정 프레임워크를 제공합니다.

REST API란?

REST(Representational State Transfer)는 Roy Fielding이 2000년 박사 논문에서 제안한 아키텍처 스타일입니다. HTTP 프로토콜을 기반으로 하며, 리소스 중심의 설계 철학을 따릅니다.

REST의 핵심 원칙

  1. 리소스 기반: 모든 것은 리소스로 표현됩니다
  2. HTTP 메서드 활용: GET, POST, PUT, DELETE 등을 의미있게 사용
  3. 무상태성(Stateless): 각 요청은 독립적이며 이전 요청과 무관
  4. 캐싱 가능: HTTP 캐싱 메커니즘 활용
  5. 계층화된 시스템: 클라이언트는 직접 서버와 통신하는지 중간 계층과 통신하는지 알 수 없음

REST API 예제

블로그 시스템의 REST API를 예로 들어보겠습니다:

# 모든 게시글 조회
GET /api/posts
Response: [
  { "id": 1, "title": "첫 번째 글", "authorId": 100 },
  { "id": 2, "title": "두 번째 글", "authorId": 101 }
]
 
# 특정 게시글 조회
GET /api/posts/1
Response: {
  "id": 1,
  "title": "첫 번째 글",
  "content": "내용...",
  "authorId": 100,
  "createdAt": "2025-09-30T10:00:00Z"
}
 
# 저자 정보 조회 (별도 요청 필요)
GET /api/users/100
Response: {
  "id": 100,
  "name": "홍길동",
  "email": "hong@example.com"
}
 
# 댓글 조회 (또 다른 요청)
GET /api/posts/1/comments
Response: [
  { "id": 1, "postId": 1, "content": "좋은 글이네요", "authorId": 102 }
]

위 예제에서 볼 수 있듯이, 게시글과 저자 정보, 댓글을 모두 가져오려면 최소 3번의 HTTP 요청이 필요합니다. 이를 N+1 문제라고 부르며, REST의 주요 단점 중 하나입니다.

GraphQL이란?

GraphQL은 API를 위한 쿼리 언어이자 런타임입니다. Facebook이 모바일 앱의 복잡한 데이터 요구사항을 해결하기 위해 개발했으며, 클라이언트가 필요한 데이터를 정확히 명시할 수 있습니다.

GraphQL의 핵심 특징

  1. 단일 엔드포인트: 보통 /graphql 하나만 사용
  2. 타입 시스템: 스키마로 API 구조를 명확히 정의
  3. 클라이언트 주도: 필요한 필드만 요청 가능
  4. 실시간 구독: WebSocket 기반 실시간 데이터 업데이트
  5. 강력한 개발 도구: GraphiQL, Apollo Studio 등

GraphQL 예제

동일한 블로그 시스템을 GraphQL로 구현하면:

# 스키마 정의
type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  comments: [Comment!]!
  createdAt: DateTime!
}
 
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}
 
type Comment {
  id: ID!
  content: String!
  author: User!
  post: Post!
}
 
type Query {
  posts: [Post!]!
  post(id: ID!): Post
  user(id: ID!): User
}
# 단일 쿼리로 모든 데이터 가져오기
query GetPostWithDetails {
  post(id: "1") {
    id
    title
    content
    createdAt
    author {
      id
      name
    }
    comments {
      id
      content
      author {
        name
      }
    }
  }
}
 
# 응답 (요청한 필드만 정확히 반환)
{
  "data": {
    "post": {
      "id": "1",
      "title": "첫 번째 글",
      "content": "내용...",
      "createdAt": "2025-09-30T10:00:00Z",
      "author": {
        "id": "100",
        "name": "홍길동"
      },
      "comments": [
        {
          "id": "1",
          "content": "좋은 글이네요",
          "author": {
            "name": "김철수"
          }
        }
      ]
    }
  }
}

단 한 번의 요청으로 게시글, 저자, 댓글 정보를 모두 가져올 수 있습니다. 또한 email 필드가 필요 없다면 요청하지 않아 불필요한 데이터 전송을 줄일 수 있습니다.

심층 비교: GraphQL vs REST

1. 데이터 페칭 효율성

REST의 문제점:

  • Over-fetching: 필요 이상의 데이터를 받음
  • Under-fetching: 여러 엔드포인트를 호출해야 함
  • 버전 관리: /api/v1/posts, /api/v2/posts 등 버전별 엔드포인트 관리 필요

GraphQL의 장점:

  • 정확히 필요한 데이터만 요청
  • 단일 요청으로 복잡한 관계 데이터 조회
  • 스키마 진화로 버전 관리 불필요 (deprecated 필드 표시)

예시 비교:

// REST: 사용자 프로필 페이지 로딩
async function loadUserProfile(userId) {
  const user = await fetch(`/api/users/${userId}`);
  const posts = await fetch(`/api/users/${userId}/posts`);
  const followers = await fetch(`/api/users/${userId}/followers`);
  const following = await fetch(`/api/users/${userId}/following`);
 
  // 4번의 HTTP 요청, 많은 불필요한 데이터 포함
  return { user, posts, followers, following };
}
 
// GraphQL: 동일한 작업
async function loadUserProfile(userId) {
  const result = await fetch('/graphql', {
    method: 'POST',
    body: JSON.stringify({
      query: `
        query UserProfile($userId: ID!) {
          user(id: $userId) {
            name
            avatar
            bio
            posts(limit: 10) {
              title
              createdAt
            }
            followerCount
            followingCount
          }
        }
      `,
      variables: { userId }
    })
  });
 
  // 1번의 요청, 필요한 데이터만 정확히 반환
  return result.data.user;
}

2. 타입 안정성과 개발 경험

REST:

  • OpenAPI/Swagger로 문서화 가능하지만 별도 작업 필요
  • 런타임에 예상치 못한 응답 구조 발생 가능
  • 타입스크립트 사용 시 수동으로 타입 정의 필요

GraphQL:

  • 스키마가 곧 문서이며 계약서
  • 타입 불일치 시 쿼리 실행 전 오류 발견
  • 코드 생성 도구로 타입스크립트 타입 자동 생성
// GraphQL Code Generator 사용 예
// schema.graphql에서 자동 생성된 타입
import { GetPostQuery, GetPostQueryVariables } from './generated/graphql';
 
const { data } = useQuery<GetPostQuery, GetPostQueryVariables>(GET_POST_QUERY, {
  variables: { id: '1' }
});
 
// data.post는 완전한 타입 안정성 보장
console.log(data.post.title); // ✅ 타입 체크됨
console.log(data.post.nonExistent); // ❌ 컴파일 오류

3. 캐싱 전략

REST의 장점:

  • HTTP 캐싱 메커니즘 그대로 사용 (ETag, Cache-Control)
  • CDN 캐싱 쉽게 적용 가능
  • 브라우저 기본 캐싱 활용

GraphQL의 도전과제:

  • POST 요청 사용으로 기본 HTTP 캐싱 불가
  • Apollo Client, URQL 등 전용 캐싱 라이브러리 필요
  • 정규화된 캐시로 복잡한 관계 데이터 관리
// Apollo Client 캐싱 설정
import { InMemoryCache } from '@apollo/client';
 
const cache = new InMemoryCache({
  typePolicies: {
    Post: {
      fields: {
        comments: {
          merge(existing = [], incoming) {
            return [...existing, ...incoming];
          }
        }
      }
    }
  }
});
 
// 동일한 데이터를 참조하는 여러 쿼리가 자동으로 동기화됨

4. 성능과 복잡도

REST:

  • 구현이 단순하고 직관적
  • 서버 부하가 예측 가능
  • Rate limiting 적용 쉬움

GraphQL:

  • 복잡한 쿼리로 서버 부하 증가 가능
  • N+1 쿼리 문제 발생 (DataLoader로 해결)
  • Query depth limiting, complexity analysis 필요
// DataLoader로 N+1 문제 해결
import DataLoader from 'dataloader';
 
const userLoader = new DataLoader(async (userIds) => {
  // 여러 userId를 한 번에 조회
  const users = await db.users.findMany({
    where: { id: { in: userIds } }
  });
 
  // userIds 순서에 맞게 정렬하여 반환
  return userIds.map(id => users.find(u => u.id === id));
});
 
// Resolver에서 사용
const resolvers = {
  Post: {
    author: (post) => userLoader.load(post.authorId)
    // 100개 게시글이 있어도 author 쿼리는 1번만 실행됨
  }
};

5. 실시간 데이터

REST:

  • Polling 또는 Server-Sent Events (SSE) 사용
  • WebSocket 별도 구현 필요

GraphQL:

  • Subscription으로 실시간 업데이트 내장
  • WebSocket 기반 표준 프로토콜
# GraphQL Subscription 예제
subscription OnCommentAdded($postId: ID!) {
  commentAdded(postId: $postId) {
    id
    content
    author {
      name
    }
  }
}
// 클라이언트에서 구독
const subscription = client.subscribe({
  query: ON_COMMENT_ADDED,
  variables: { postId: '1' }
}).subscribe({
  next: ({ data }) => {
    console.log('새 댓글:', data.commentAdded);
    // UI 자동 업데이트
  }
});

장단점 요약

REST API

장점:

  • 학습 곡선이 낮고 이해하기 쉬움
  • HTTP 캐싱 메커니즘 활용 가능
  • 광범위한 툴과 라이브러리 생태계
  • 파일 업로드/다운로드 간단
  • Stateless 아키텍처로 확장성 좋음

단점:

  • Over-fetching/Under-fetching 문제
  • 여러 엔드포인트 호출 필요
  • 버전 관리의 복잡성
  • 모바일 환경에서 비효율적 (여러 요청)

GraphQL

장점:

  • 정확한 데이터 페칭으로 네트워크 효율성 극대화
  • 강력한 타입 시스템과 자기 문서화
  • 단일 엔드포인트로 복잡한 쿼리 처리
  • 실시간 구독 내장
  • 빠른 프론트엔드 개발 (Mock 데이터 쉬움)

단점:

  • 학습 곡선이 높음 (스키마, Resolver, DataLoader 등)
  • 서버 구현 복잡도 증가
  • HTTP 캐싱 활용 어려움
  • 쿼리 복잡도 관리 필요
  • 파일 업로드 복잡함 (별도 명세 필요)

의사결정 프레임워크: 언제 무엇을 선택할까?

REST를 선택해야 하는 경우

  1. 단순한 CRUD API

    • 리소스가 명확히 구분됨
    • 복잡한 관계 데이터가 적음
    • 예: 간단한 TODO 앱, 공지사항 게시판
  2. 높은 캐싱 요구사항

    • CDN 캐싱이 중요한 공개 API
    • 정적 콘텐츠가 많음
    • 예: 뉴스 사이트, 블로그
  3. 팀의 경험과 학습 곡선

    • GraphQL 경험이 없는 팀
    • 빠른 프로토타이핑이 필요한 스타트업
    • 레거시 시스템과의 호환성
  4. 파일 처리가 주요 기능

    • 이미지/동영상 업로드가 핵심
    • 대용량 파일 다운로드
    • 예: 파일 공유 서비스

GraphQL을 선택해야 하는 경우

  1. 복잡한 관계형 데이터

    • 여러 엔티티 간 깊은 관계
    • 클라이언트마다 다른 데이터 요구사항
    • 예: 소셜 네트워크, 전자상거래 플랫폼
  2. 모바일 최적화

    • 네트워크 비용과 배터리 소모 최소화
    • 여러 요청을 하나로 통합
    • 예: 모바일 우선 애플리케이션
  3. 빠른 프론트엔드 반복

    • 백엔드 수정 없이 UI 변경
    • A/B 테스팅으로 다양한 데이터 조합 실험
    • 예: 데이터 중심 대시보드
  4. 실시간 기능

    • 채팅, 알림, 협업 도구
    • 구독 기반 실시간 업데이트
    • 예: Slack, Notion 같은 협업 툴
  5. 마이크로서비스 통합

    • 여러 서비스의 데이터를 단일 API로 통합
    • GraphQL Federation 활용
    • 예: 대규모 엔터프라이즈 시스템

하이브리드 접근

두 방식을 혼합하는 것도 가능합니다:

# 사용 사례별 분리
- GraphQL: 프론트엔드 애플리케이션용 (복잡한 쿼리)
- REST: 외부 API (간단한 공개 엔드포인트)
- REST: 파일 업로드/다운로드 전용
 
# 점진적 마이그레이션
- 기존 REST API 유지
- 새로운 복잡한 기능은 GraphQL로 구현
- Apollo의 RESTDataSource로 REST → GraphQL 래핑

실전 예제: 전자상거래 API 설계

REST 설계

# 상품 목록
GET /api/products?page=1&limit=20
 
# 상품 상세 (5번의 요청 필요)
GET /api/products/123
GET /api/products/123/reviews
GET /api/products/123/related
GET /api/sellers/456  # 판매자 정보
GET /api/categories/789  # 카테고리 정보
 
# 장바구니
POST /api/cart/items
GET /api/cart
DELETE /api/cart/items/1

GraphQL 설계

# 상품 상세 (1번의 요청)
query ProductDetail($id: ID!) {
  product(id: $id) {
    id
    name
    price
    images
    seller {
      name
      rating
    }
    category {
      name
      breadcrumb
    }
    reviews(first: 10) {
      rating
      comment
      author {
        name
      }
    }
    relatedProducts(limit: 5) {
      id
      name
      price
      thumbnail
    }
  }
}
 
# Mutation으로 장바구니 관리
mutation AddToCart($productId: ID!, $quantity: Int!) {
  addToCart(productId: $productId, quantity: $quantity) {
    cart {
      items {
        product {
          name
          price
        }
        quantity
      }
      totalPrice
    }
  }
}

위 예제에서 GraphQL은 상품 상세 페이지 로딩을 5배 빠르게 만들 수 있습니다 (5번 요청 → 1번 요청).

마이그레이션 전략

REST → GraphQL

  1. GraphQL 레이어 추가: 기존 REST를 GraphQL로 래핑
  2. 점진적 전환: 새로운 기능부터 GraphQL로 구현
  3. 클라이언트 선택: 모바일은 GraphQL, 웹은 REST 유지 가능
  4. 성능 모니터링: DataLoader, 쿼리 복잡도 추적

GraphQL → REST

(드문 경우지만)

  1. 복잡도 감소: 쿼리가 과도하게 복잡해진 경우
  2. 캐싱 최적화: CDN 활용이 필수인 공개 API
  3. 리소스 단위 엔드포인트 추출: 자주 사용되는 쿼리를 REST로

결론

GraphQL과 REST는 각각의 강점이 있으며, "어느 것이 더 좋다"는 질문에 정답은 없습니다. 핵심은 프로젝트의 요구사항, 팀의 역량, 유지보수 비용을 종합적으로 고려하는 것입니다.

간단한 가이드라인:

  • 간단한 API → REST
  • 복잡한 관계형 데이터 + 모바일 → GraphQL
  • 공개 API + 높은 캐싱 → REST
  • 실시간 + 빠른 반복 → GraphQL

가장 중요한 것은 성급한 최적화를 피하고, 현재 문제를 해결하는 데 집중하는 것입니다. REST로 시작해서 필요할 때 GraphQL로 마이그레이션하거나, 두 가지를 혼합해서 사용하는 것도 완전히 유효한 전략입니다.

참고 자료

  • GraphQL 공식 문서
  • REST API Tutorial
  • Apollo GraphQL
  • Roy Fielding's REST Dissertation
  • GraphQL Best Practices

목차

  • GraphQL vs REST: 언제 무엇을 사용할까
  • REST API란?
  • GraphQL이란?
  • 심층 비교: GraphQL vs REST
  • 장단점 요약
  • 의사결정 프레임워크: 언제 무엇을 선택할까?
  • 실전 예제: 전자상거래 API 설계
  • 마이그레이션 전략
  • 결론
  • 참고 자료